#include "App_Sound_Vido.h"
#include "Dri_WebSocket.h"
#include "Inf_ES8311.h"
#include "Inf_OV2640.h"
#include "freertos/ringbuf.h"
#include "string.h"


static const char *TAG = "App_Sound_Vido";

// 定义WebSocket客户端句柄 语音
static esp_websocket_client_handle_t s_Sound_client;
// 定义WebSocket客户端句柄 视频
static esp_websocket_client_handle_t video_client;

// 环形缓冲区的操作句柄
static RingbufHandle_t ringbuffer;

// 定义两个标志位用于控制ESP2CLIENT以及CLIENT2ESP打开的音频连接
volatile uint8_t is_receing = 0;
volatile uint8_t is_sending = 0;

// 定义一个标志位用于删除任务判断
volatile uint8_t del_tasks = 0;
// 视频是否删除
volatile uint8_t del_video_task = 0;

///////////////////////////// 任务
// 创建任务将采集的数据发送到缓存
void task_DataToCache(void *arg);
// 创建句柄
TaskHandle_t task_DataToCache_handle = NULL;

// 将缓存的数据发送到WebSocket客户端
void task_CacheToWebSocket(void *arg);
// 创建句柄
TaskHandle_t task_CacheToWebSocket_handle = NULL;

// 视频任务
void Inf_WS_VIDEO_Task(void *args);
TaskHandle_t video_task_handler;
////////////

/**
 * @brief  初始化声音
 *
 * @param dir
 */
void App_WebSocket_Init(void) {
  // 初始化WebSocket客户端配置结构体
  esp_websocket_client_config_t websocket_cfg = {};

  // 设置WebSocket的连接URI，从配置中获取
  websocket_cfg.uri = CONFIG_WEBSOCKET_URI;

  // 打印日志，显示正在连接的URI
  ESP_LOGI(TAG, "正在连接的URI to %s...", websocket_cfg.uri);

  // 根据配置初始化WebSocket客户端
  s_Sound_client = esp_websocket_client_init(&websocket_cfg);
  // 注册WebSocket事件处理函数，处理所有类型的WebSocket事件
  // WEBSOCKET_EVENT_ANY = -1 表示任意 WebSocket 事件。当注册事件处理函数时，如果使用这个值，意味着该处理函数会处理所有类型的 WebSocket 事件。
  esp_websocket_register_events(s_Sound_client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)s_Sound_client);

  // 配置视频客户端的连接地址
  websocket_cfg.uri = CONFIG_VIDO_WEBSOCKET_URI;

  // 初始化视频客户端
  video_client = esp_websocket_client_init(&websocket_cfg);

  // 注册回调函数
  esp_websocket_register_events(video_client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)video_client);

  // 创建环形缓冲区
  ringbuffer = xRingbufferCreate(32 * 1024, RINGBUF_TYPE_NOSPLIT);
}

/**
 * @brief 开启 声音 WebSocket客户端
 *
 * @param dir
 */
void App_WebSocket_Sound_Start(uint8_t dir) {
  // 检查声音的WebSocket客户端句柄是否不为空，并且客户端当前未连接
  if (s_Sound_client != NULL && !esp_websocket_client_is_connected(s_Sound_client)) {
    // 若客户端已初始化但未连接，则启动声音的WebSocket客户端
    esp_err_t err = esp_websocket_client_start(s_Sound_client);
    MY_LOGE("启动声音的WebSocket客户端, err = %d", err);
  }

  // 判断是不是 单片机获取声音
  if (dir == SOUND_DIR_ESP2CLIENT) {
    is_sending = 1;
    del_tasks = 0;
    // 创建任务用于从MIC读取数据放到缓存
    xTaskCreate(task_DataToCache, "mic_buffer_task", 4096, NULL, 5, &task_DataToCache_handle);

    // 创建任务用于从缓存读取数据通过WS客户端发送出去
    xTaskCreate(task_CacheToWebSocket, "buffer_ws_task", 4096, NULL, 5, &task_CacheToWebSocket_handle);
  }
  if (dir == SOUND_DIR_CLIENT2ESP) {
    is_receing = 1;
  }
}

// 创建任务将采集的数据发送到缓存
void task_DataToCache(void *arg) {

  printf("创建 MIwerC - > Buffer 任务...\r\n");
  char data[512 - 8] = {0};

  while (1) {
    // MY_LOGE("123\r\n");
    // 读取MIC数据
    // 采样率:16K Hz(次/秒)
    // 位深:16位  2 Byte
    // 1秒钟采集的数据多大:32K Byte
    // 10ms采集的数据多大:320 Byte
    Inf_ES8311_ReceData(data, sizeof(data));
    //  MY_LOGE("123\r\n");
    // // 写入环形缓冲区
    xRingbufferSend(ringbuffer, data, sizeof(data), portMAX_DELAY);
    vTaskDelay(10);
  }
}

// 将缓存的数据发送到WebSocket客户端
void task_CacheToWebSocket(void *arg) {
  int len = 0;
  printf("创建 MICwerw - > Buffer 任务...\r\n");
  while (1) {
    // MY_LOGE("taet 任务...  %d\r\n", len);
    // 读取环形缓冲区的数据
    char *data = xRingbufferReceive(ringbuffer, (size_t *)&len, portMAX_DELAY);
    // MY_LOGE("taet 任务...  %d\r\n", len);
    if (len > 0) {
      // MY_LOGE("taet 任务...  %d\r\n", len);
      //  通过WS客户端发送出去
      App_WebSocket_Sound_AddData(s_Sound_client, data, len);
      // 将内部申请的内存进行回收
      vRingbufferReturnItem(ringbuffer, data);
    }
    vTaskDelay(10);
  }
}

/**
 * @brief  关闭  声音 WebSocket客户端
 *
 * @param dir
 */
void App_WebSocket_Sound_Stop(uint8_t dir) {

  if (dir == SOUND_DIR_ESP2CLIENT) {
    // 修改标志位,让任务自杀
    del_tasks = 2;
    is_sending = 0;
  }
  if (dir == SOUND_DIR_CLIENT2ESP) {
    is_receing = 0;
  }

  // 检查声音的WebSocket客户端句柄是否不为空，并且客户端当前连接
  if (s_Sound_client != NULL && esp_websocket_client_is_connected(s_Sound_client)) {
    // 关闭WebSocket客户端
    esp_websocket_client_close(s_Sound_client, portMAX_DELAY);
    // 打印日志，显示WebSocket已停止
    ESP_LOGI(TAG, "打印日志,显示WebSocket已停止");
  }
}

/**
 * @brief  声音 添加数据
 *
 * @param client websocket客户端句柄
 * @param data 数据
 * @param len 数据长度
 */
void App_WebSocket_Sound_AddData(esp_websocket_client_handle_t client, char *data, uint32_t len) {
  if (client != NULL && esp_websocket_client_is_connected(client) && len > 0) {
    // 通过WS客户端发送出去
    esp_websocket_client_send_bin(client, data, len, portMAX_DELAY);
  }
}

/**
 * @brief 打开 vido  WebSocket客户端连接
 *
 */
void App_WS_Video_Client_Start(void) {
  // 视频客户端已经初始化且未连接
  if (video_client != NULL && !esp_websocket_client_is_connected(video_client)) {
    del_video_task = 0;
    printf("开启视频连接...\r\n");
    esp_websocket_client_start(video_client);

    if (video_task_handler == NULL) {
      xTaskCreate(Inf_WS_VIDEO_Task, "video_task", 8192, NULL, 24, &video_task_handler);
    }
  }
}

/**
 * @description: 视频任务
 * @param {void} *args
 * @return {*}
 */
void Inf_WS_VIDEO_Task(void *args) {
  printf("开启视频任务...\r\n");
  uint8_t *data;
  size_t len = 0;
  while (1) {
    // 使用摄像头驱动拍照
    Inf_OV2640_GetPicture(&data, &len);

    // 通过WS客户端发送至服务器
    App_WebSocket_Sound_AddData(video_client, (char *)data, len);

    // 释放内存
    Inf_OV2640_FreePicture();

    // 判断是否需要自杀
    if (del_video_task) {
      printf("删除视频任务...\r\n");
      vTaskDelete(NULL);
    }

    vTaskDelay(10);
  }
}

/**
 * @brief 关闭 vido  WebSocket客户端连接
 *
 */
void App_WS_Video_Client_Stop(void) {
  // 视频客户端已经初始化且已连接
  if (video_client != NULL && esp_websocket_client_is_connected(video_client)) {
    printf("关闭视频连接...\r\n");
    esp_websocket_client_close(video_client, portMAX_DELAY);

    del_video_task = 1;
  }
}